package com.gtt.jr.repository.impl.base;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.gtt.jr.repository.base.BaseDao;
import com.gtt.jr.repository.domain.base.BaseDomain;

@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public class BaseDaoImpl<T extends BaseDomain> extends HibernateDaoSupport
		implements BaseDao<T> {

	private Class<T> clazz;

	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		this.clazz = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	@Autowired
	public void init(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}

	// ---------Load method

	@SuppressWarnings("unchecked")
	public T load(Long id) {
		return (T) getSession().get(this.clazz, id);
	}

	public T load(String propertyName, Object propertyValue) {
		return load(new String[] { propertyName },
				new Object[] { propertyValue });
	}

	@SuppressWarnings("unchecked")
	public T load(String[] propertyNames, Object[] propertyValues) {
		Criteria criteria = generateCriteria(propertyNames, propertyValues);
		return criteria == null ? null : (T) criteria.uniqueResult();
	}

	// ------------Find method

	@SuppressWarnings("unchecked")
	public List<T> findAll(String... orderProperties) {
		Criteria criteria = getSession().createCriteria(clazz);
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		if (orderProperties != null) {
			for (String orderProperty : orderProperties) {
				criteria.addOrder(Order.desc(orderProperty));
			}
		}
		return criteria.list();
	}

	public List<T> find(String propertyName, Object propertyValue) {
		return find(new String[] { propertyName },
				new Object[] { propertyValue });
	}

	@SuppressWarnings("unchecked")
	public List<T> find(String[] propertyNames, Object[] propertyValues) {
		Criteria criteria = generateCriteria(propertyNames, propertyValues);
		return criteria == null ? null : criteria.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> find(String queryStr) {
		Query query = getSession().createQuery(queryStr);
		return query.list();
	}

	@SuppressWarnings("unchecked")
	public List find(String queryStr, Object[] positionalParameters) {
		Query query = getSession().createQuery(queryStr);
		if (positionalParameters != null) {
			for (int i = 0; i < positionalParameters.length; i++) {
				Object positionalParameter = positionalParameters[i];
				query.setParameter(i, positionalParameter);
			}
		}
		return query.list();
	}

	@SuppressWarnings( { "unchecked", })
	public List<T> find(Class<T> clazz, String queryStr,
			Object[] positionPrameters) {
		List result = find(queryStr, positionPrameters);
		return result == null ? null : (List<T>) result;
	}

	// @SuppressWarnings("rawtypes")
	// public Object findUnique(String queryStr, Object[] positionPrameters) {
	// List result = find(queryStr, positionPrameters);
	// return result == null ? null : result.get(0);
	// }

	// --------add/delete

	@Transactional(readOnly = false)
	public void save(T entity) {
		getSession().saveOrUpdate(entity);
	}

	@Transactional(readOnly = false)
	public void delete(Long id) {
		getSession().delete(load(id));
	}
	
	@Transactional(readOnly = false)
	public void delete(T entity) {
		getSession().delete(entity);
	}

	private Criteria generateCriteria(String[] propertyNames,
			Object[] propertyValues) {
		Criteria criteria = null;
		if (propertyNames != null && propertyValues != null
				&& propertyNames.length == propertyValues.length) {
			criteria = getSession().createCriteria(clazz);
			criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
			for (int i = 0; i < propertyNames.length; i++) {
				Object value = propertyValues[i];
				if (value == null) {
					criteria.add(Restrictions.isNull(propertyNames[i]));
					continue;
				}
				criteria.add(Restrictions.eq(propertyNames[i],
						propertyValues[i]));
			}
		}
		return criteria;
	}

}
